'''
GuaCombination 类用法说明
1. 初始化类
python

gc = GuaCombination(m, dg, dz, gua, bian_gua=None)

参数说明：

    m: 月份 (0-11)

    dg: 天干日 (0-9)

    dz: 地支日 (0-11)，必须与 dg 奇偶相同

    gua: 主卦 (0-63)

    bian_gua: 变卦 (0-63 或 None)

示例：
python



2. 属性与方法
方法/属性	功能	返回值
to_str	生成卦组合字符串	"5335217"
to_list	返回属性列表	[5, 3, 3, 42, 15, '5335217', 1946463]
to_dict	返回属性词典	{'m': 5, 'dg': 3, 'd': 3, 'gua': 42, 'biangua': 15, 'str': '5335217', 'idx': 1946463}
to_idx	计算当前实例的索引	1946463
from_gua_str(gua_str)	从字符串解析实例	GuaCombination
from_dict(dct)	从字典创建实例	GuaCombination
get_index4(GG, BB, M, DD)	计算4参数索引	int
get_index5(M, DG, DZ, GG, BG)	计算5参数索引	int
from_index4(index)	给索引反向计算4参数	(GG, BB, M, DD)
from_index5(index)	给索引反向计算5参数	(M, DG, DZ, GG, BG)

'''
class GuaCombination:
    _total_m_dd = 12 * 60
    _total_bb_m_dd = 64 * 12 * 60
    _min_idx = 0
    _max_idx = 2949119

    def __init__(self, m, dg, d, gua, biangua=None):
        """
        初始化卦组合
        :param m: 月份 (0-11)
        :param dg: 天干日 (0-9)
        :param d: 地支日 (0-11)
        :param gua: 卦 (0-63)
        :param biangua: 变卦 (0-63 或 None)
        """
        assert 0 <= m <= 11, "month out of range"
        assert 0 <= dg <= 9, "day_g out of range"
        assert 0 <= d <= 11, "day_z out of range"
        assert dg % 2 == d % 2, "干支必须奇偶相同"
        assert 0 <= gua <= 63, "gua out of range"
        assert biangua is None or 0 <= biangua <= 63, "bian_gua out of range or is None"

        self.month = m
        self.day_g = dg
        self.day_z = d
        self.gua = gua
        self.bian_gua = biangua

    @staticmethod
    def get_char(value)->str:
        """辅助方法：将数字转换为字符表示"""
        if value < 10:
            return str(value)
        elif value == 10:
            return 'A'
        elif value == 11:
            return 'B'
        else:
            raise ValueError(f"Value out of range:{value}")

    @staticmethod
    def char_to_int(char)->int:
        if char.isdigit():
            return int(char)
        elif char.upper() in ['A', 'B']:
            return {'A': 10, 'B': 11}
        else:
            raise ValueError(f"char out of range:{char}")

    @property
    def to_str(self):
        """
        生成卦组合字符串
        比喻：卦的身份证号
        格式: 月份(1位)天干日(1位)地支日(1位)卦上(1位)卦下(1位)变卦部分(2位)
        """
        # 主卦部分
        gua_gong = self.gua // 8
        gua_sequence = self.gua % 8

        # 变卦部分
        if self.bian_gua is not None and self.gua != self.bian_gua:
            B1 = self.bian_gua // 8
            B2 = self.bian_gua % 8
            BG_part = self.get_char(B1) + self.get_char(B2)
        else:
            BG_part = "XX"

        # 构建字符串
        str_value = (f"{self.get_char(self.month)}{self.day_g}"
                     f"{self.get_char(self.day_z)}{gua_gong}{gua_sequence}{BG_part}")
        return str_value

    @property
    def to_idx(self):
        '''卦的身份证号（纯数字）'''
        return self.get_index5(self.month, self.day_g, self.day_z, self.gua, self.bian_gua)

    @property
    def to_list(self):
        """返回一个列表，包含实例的属性值"""
        return [self.month, self.day_g, self.day_z, self.gua, self.bian_gua, self.to_str, self.to_idx]

    @property
    def to_dict(self):
        return {m: n for m, n in zip(['m', 'dg', 'd', 'gua', 'biangua', 'str', 'idx'], self.to_list)}

    def __str__(self):
        return str(self.to_dict)


    '''创建新实例的方法，从字符串/词典创建实例'''

    @classmethod
    def from_gua_str(cls, gua_str):
        """从卦字符串创建实例"""
        gua_str = gua_str.upper()
        if len(gua_str) != 7:
            raise ValueError("Invalid gua_str length")

        # 解析各部分
        month = cls.char_to_int(gua_str[0])  # 月份(0-11)
        day_g = int(gua_str[1])  # 天干日(0-9)
        day_z = cls.char_to_int(gua_str[2])  # 地支日(0-11)

        # 主卦
        gua_gong = int(gua_str[3])
        gua_sequence = int(gua_str[4])
        gua = gua_gong * 8 + gua_sequence

        # 变卦
        bg_part = gua_str[5:7]
        if bg_part == "XX":
            bian_gua = None
        else:
            B1 = int(bg_part[0], 12)
            B2 = int(bg_part[1], 12)
            bian_gua = B1 * 8 + B2

        return GuaCombination(month, day_g, day_z, gua, bian_gua)

    @staticmethod
    def from_dict(dct):
        '''从字典创建实例'''
        m = dct['m']
        dg = dct['dg']
        dz = dct['d']
        gua = dct['gua']
        bian_gua = dct['biangua']
        return GuaCombination(m, dg, dz, gua, bian_gua)

    '''计算给定4/5参数的索引'''

    @staticmethod
    def get_index4(M, DD, GG, BB):
        """
        计算4参数索引(GG, BB, M, DD)
        GG: 卦 (0-63)
        BB: 变卦 (0-63)
        M: 月份 (0-11)
        DD: 日 (0-59)
        """
        assert 0 <= GG <= 63, "GG out of range"
        assert 0 <= BB <= 63, "BB out of range"
        assert 0 <= M <= 11, "M out of range"
        assert 0 <= DD <= 59, "DD out of range"
        return GG * (64 * 12 * 60) + BB * (12 * 60) + M * 60 + DD

    @staticmethod
    def get_index5(M, DG, DZ, GG, BG):
        """
        计算5参数索引(M, DG, DZ, GG, BG)
        先将DG和DZ转换为DD，再应用get_index4
        """
        assert DG % 2 == DZ % 2, f"干支必须奇偶相同，M, DG, DZ, GG, BG:{M, DG, DZ, GG, BG}"

        # 找到满足 DG ≡ DD mod 10 且 DZ ≡ DD mod 12 的DD
        DD = None
        for d in range(60):
            if d % 10 == DG and d % 12 == DZ:
                DD = d
                break

        if DD is None:
            raise ValueError("Invalid day_g/day_z combination")

        return GuaCombination.get_index4(M, DD, GG, BG if BG is not None else GG)

    '''从索引反向计算出参数组合'''

    @classmethod
    def from_index4(cls,index)->tuple:
        """从4参数索引反向计算组合"""
        assert isinstance(index, int),f"应该输入整数，index:{index},type{type(index)}"
        assert cls._min_idx <= index <= cls._max_idx, f"Invalid index,is {cls._min_idx} <= {index} <= {cls._max_idx}?"

        GG = index // cls._total_bb_m_dd
        remainder = index % cls._total_bb_m_dd

        BB = remainder // cls._total_m_dd
        remainder = remainder % cls._total_m_dd

        M = remainder // 60
        DD = remainder % 60

        return M, DD, GG, BB

    @classmethod
    def from_index5(cls,index)->tuple:
        """从5参数索引反向计算组合，分别是m,dg,dz,gua,biangua"""
        assert isinstance(index, int)
        assert cls._min_idx <= index <= cls._max_idx, f"Invalid index,is {cls._min_idx} <= {index} <= {cls._max_idx}?"
        M, DD, GG, BB = GuaCombination.from_index4(index)

        # 计算天干地支
        DG = DD % 10
        DZ = DD % 12

        # 如果变卦与主卦相同，设为None
        BG = BB if BB != GG else None

        return M, DG, DZ, GG, BG


